package com.cheer.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cheer.aop.PreAcquireLock;
import com.cheer.bo.ExVotePersonBO;
import com.cheer.dao.ExVoteCategoryDao;
import com.cheer.dao.ExVoteHistoryDao;
import com.cheer.dao.ExVotePersonDao;
import com.cheer.dao.ExVotePersonExtraDao;
import com.cheer.entity.ExVoteCategoryEntity;
import com.cheer.entity.ExVoteHistoryEntity;
import com.cheer.entity.ExVotePersonEntity;
import com.cheer.entity.ExVotePersonExtraEntity;
import com.cheer.exception.ServiceException;
import com.cheer.param.PersonSaveParam;
import com.cheer.service.VoteService;
import com.cheer.vo.ExVoteIndexVO;
import com.cheer.vo.ExVotePersonVO;
import com.cheer.vo.PhotoVO;
import com.google.common.collect.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class VoteServiceImpl implements VoteService, ApplicationListener<ApplicationReadyEvent> {

    private List<ExVoteCategoryEntity> categories;

    private Map<Long, List<ExVotePersonBO>> persons = new HashMap<>();

    private Map<Long, ExVotePersonBO> personCache = new HashMap<>();

    @Value("${activity.id}")
    private Long activityId;

    @Value("${image.url}")
    private String imageUrl;

    @Autowired
    private ExVoteCategoryDao exVoteCategoryDao;

    @Autowired
    private ExVotePersonDao exVotePersonDao;

    @Autowired
    private ExVotePersonExtraDao exVotePersonExtraDao;

    @Autowired
    private ExVoteHistoryDao exVoteHistoryDao;

    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        categories = exVoteCategoryDao.selectList(
                new QueryWrapper<ExVoteCategoryEntity>().eq("activity_id", activityId));

        List<ExVotePersonEntity> persons = exVotePersonDao.selectList(
                new QueryWrapper<ExVotePersonEntity>().eq("activity_id", activityId).orderByAsc("person_number"));

        if (persons.size() > 0) {
            List<Long> personIds = persons.stream().map(ExVotePersonEntity::getId).collect(Collectors.toList());
            List<ExVotePersonExtraEntity> personExtras = exVotePersonExtraDao.selectList(
                    new QueryWrapper<ExVotePersonExtraEntity>().in("person_id", personIds));
            Map<Long, ExVotePersonExtraEntity> personExtraMap = personExtras.stream()
                    .collect(Collectors.toMap(ExVotePersonExtraEntity::getPersonId, Function.identity()));
            for (ExVotePersonEntity person : persons) {
                ExVotePersonBO personBO = new ExVotePersonBO();
                BeanUtil.copyProperties(person, personBO);
                personBO.setHeadIcon(imageUrl + personBO.getHeadIcon());
                ExVotePersonExtraEntity personExtra = personExtraMap.get(person.getId());
                if (personExtra != null) {
                    personBO.setIntro(personExtra.getIntro());
                    personBO.setStory(personExtra.getStory());
                }
                this.personCache.put(personBO.getId(), personBO);
                if (this.persons.containsKey(person.getCategoryId())) {
                    this.persons.get(person.getCategoryId()).add(personBO);
                } else {
                    this.persons.put(person.getCategoryId(), Lists.newArrayList(personBO));
                }
            }
        }
    }

    @Override
    public List<ExVoteIndexVO> index(String openid) {
        List<ExVoteIndexVO> indexList = new ArrayList<>();

        List<ExVotePersonEntity> persons = exVotePersonDao.selectList(
                new QueryWrapper<ExVotePersonEntity>().select("id", "vote_count")
                        .eq("activity_id", activityId).orderByAsc("person_number"));
        Map<Long, Integer> voteCountMap = persons.stream()
                .collect(Collectors.toMap(ExVotePersonEntity::getId, ExVotePersonEntity::getVoteCount));

        Set<Long> personIdsVoted = getPersonIdsVoted(openid);

        for (ExVoteCategoryEntity category : categories) {
            ExVoteIndexVO index = new ExVoteIndexVO();
            index.setCategoryName(category.getCategoryName());
            index.setPersons(new ArrayList<>());
            indexList.add(index);

            if (!this.persons.containsKey(category.getId())) {
                continue;
            }

            List<ExVotePersonBO> personBOs = this.persons.get(category.getId());
            for (ExVotePersonBO personBO : personBOs) {
                ExVotePersonVO exVotePersonVO = getPersonVO(voteCountMap, personIdsVoted, personBO);
                index.getPersons().add(exVotePersonVO);
            }
        }

        return indexList;
    }

    private Set<Long> getPersonIdsVoted(String openid) {
        Date today = DateUtil.beginOfDay(new Date());
        List<ExVoteHistoryEntity> personsVoted = exVoteHistoryDao.selectList(new QueryWrapper<ExVoteHistoryEntity>()
                .select("person_id")
                .eq("openid", openid)
                .ge("create_time", today));
        return personsVoted.stream()
                .map(ExVoteHistoryEntity::getPersonId)
                .collect(Collectors.toSet());
    }

    private ExVotePersonVO getPersonVO(Map<Long, Integer> voteCountMap, Set<Long> personIdsVoted, ExVotePersonBO personBO) {
        ExVotePersonVO exVotePersonVO = new ExVotePersonVO();
        exVotePersonVO.setId(personBO.getId())
                .setHeadIcon(personBO.getHeadIcon())
                .setPersonName(personBO.getPersonName())
                .setPersonNumber(personBO.getPersonNumber())
                .setRemark(personBO.getRemark());
        if (voteCountMap.containsKey(personBO.getId())) {
            exVotePersonVO.setVoteCount(voteCountMap.get(personBO.getId()));
        }
        exVotePersonVO.setVoteStatus(!personIdsVoted.contains(personBO.getId()));
        return exVotePersonVO;
    }

    @Override
    public List<ExVotePersonVO> search(String keyword, String openid) {
        if (keyword == null) {
            return new ArrayList<>();
        }

        List<ExVotePersonBO> persons = personCache.values().stream()
                .filter(exVotePersonVO -> exVotePersonVO.getPersonNumber().equals(keyword) ||
                        exVotePersonVO.getPersonName().contains(keyword))
                .collect(Collectors.toList());

        if (persons.size() == 0) {
            return new ArrayList<>();
        }

        List<Long> personIds = persons.stream()
                .map(ExVotePersonBO::getId)
                .collect(Collectors.toList());

        Set<Long> personIdsVoted = getPersonIdsVoted(openid);

        Map<Long, Integer> voteCountMap = exVotePersonDao.selectList(new QueryWrapper<ExVotePersonEntity>()
                .select("id", "vote_count")
                .in("id", personIds)).stream()
                .collect(Collectors.toMap(ExVotePersonEntity::getId, ExVotePersonEntity::getVoteCount));

        return persons.stream()
                .map(personBO -> getPersonVO(voteCountMap, personIdsVoted, personBO))
                .collect(Collectors.toList());
    }

    @Override
    @PreAcquireLock(value = "#{openid}")
    public Integer postVote(Long personId, String openid) {
        Date today = DateUtil.beginOfDay(new Date());
        List<ExVoteHistoryEntity> personsVoted = exVoteHistoryDao.selectList(new QueryWrapper<ExVoteHistoryEntity>()
                .select("person_id")
                .eq("openid", openid)
                .ge("create_time", today));

        // 截止日期功能
        Date endDate = DateUtil.parse("2022-11-21 00:00:00", DatePattern.NORM_DATETIME_PATTERN);
        if(new Date().compareTo(endDate) > 0){
            throw new ServiceException("当前活动已经截止，感谢您的支持！");
        }
        // 根据分类ID下的类型 隔离
        personsVoted = getByCategoryId(personId, personsVoted);

        Set<Long> personIdsVoted = personsVoted.stream()
                .map(ExVoteHistoryEntity::getPersonId)
                .collect(Collectors.toSet());

        if(personIdsVoted.contains(personId)){
            if(personIdsVoted.size() >= 11){
                throw new ServiceException("每个序列最多可以给10个作品点赞");
            }
        }else{
            if(personIdsVoted.size() + 1 >= 11){
                throw new ServiceException("每个序列最多可以给10个作品点赞");
            }
        }
        // 每个活动点赞一次
        if (personsVoted.size() > 0) {
            Map<Long, List<ExVoteHistoryEntity>> map = personsVoted.stream().collect(Collectors.groupingBy(ExVoteHistoryEntity::getPersonId));
            List<ExVoteHistoryEntity> exVoteHistoryEntities = map.get(personId);
            if (CollectionUtil.isNotEmpty(exVoteHistoryEntities) && exVoteHistoryEntities.size()+1 > 5) {
                throw new ServiceException("每个作品最多可点赞5次");
            }
        }
        if (personsVoted.size()+1 > 10) {
            throw new ServiceException("您本序列的点赞次数已用光了");
        }

        ExVoteHistoryEntity insertVoteHistory = new ExVoteHistoryEntity();
        insertVoteHistory.setOpenid(openid);
        insertVoteHistory.setPersonId(personId);
        exVoteHistoryDao.insert(insertVoteHistory);
        exVotePersonDao.incrVote(personId);

        return 9 - personsVoted.size();
    }


    /*@Override
    @PreAcquireLock(value = "#{openid}")
    @Transactional
    public Integer postVote(Long personId, String openid) {
        Date today = DateUtil.beginOfDay(new Date());
        List<ExVoteHistoryEntity> personsVoted = exVoteHistoryDao.selectList(new QueryWrapper<ExVoteHistoryEntity>()
                .select("person_id")
                .eq("openid", openid)
                .ge("create_time", today));

        // 根据 活动ID 进行隔离
        personsVoted = getByActivityId(personsVoted, activityId);

        // 截止日期功能
        Date endDate = DateUtil.parse("2022-11-30 00:00:00", DatePattern.NORM_DATETIME_PATTERN);
        if(new Date().compareTo(endDate) > 0){
            throw new ServiceException("当前活动已经截止，感谢您的支持！");
        }
        // 根据分类ID下的类型 隔离
        personsVoted = getByCategoryId(personId, personsVoted);

        Set<Long> personIdsVoted = personsVoted.stream()
                .map(ExVoteHistoryEntity::getPersonId)
                .collect(Collectors.toSet());

        if(personIdsVoted.contains(personId)){
            if(personIdsVoted.size() >= 11){
                throw new ServiceException("每个序列最多可以给10个作品点赞");
            }
        }else{
            if(personIdsVoted.size() + 1 >= 11){
                throw new ServiceException("每个序列最多可以给10个作品点赞");
            }
        }
        // 每个活动只能点赞10次
        if (personsVoted.size() > 0) {
            Map<Long, List<ExVoteHistoryEntity>> map = personsVoted.stream().collect(Collectors.groupingBy(ExVoteHistoryEntity::getPersonId));
            List<ExVoteHistoryEntity> exVoteHistoryEntities = map.get(personId);
            if (CollectionUtil.isNotEmpty(exVoteHistoryEntities)
                    && exVoteHistoryEntities.size()+1 > 10) {
                throw new ServiceException("每个作品最多可点赞10次");
            }
        }
        if (personsVoted.size()+1 > 100) {
            //throw new ServiceException("您今天点赞次数已用光");
            throw new ServiceException("您本序列的点赞次数已用光了");
        }

        ExVoteHistoryEntity insertVoteHistory = new ExVoteHistoryEntity();
        insertVoteHistory.setOpenid(openid);
        insertVoteHistory.setPersonId(personId);
        exVoteHistoryDao.insert(insertVoteHistory);

        exVotePersonDao.incrVote(personId);

        return 99 - personsVoted.size();
    }*/

    /**
     * 根据活动类型获取点赞
     * @param personsVoted
     * @param actId
     */
    private List<ExVoteHistoryEntity> getByActivityId(List<ExVoteHistoryEntity> personsVoted, Long actId) {

        List<ExVotePersonEntity>  exVotePersonEntities = exVotePersonDao.selectByActivityId(actId);
        List<Long> personIds = exVotePersonEntities.stream().map(ExVotePersonEntity::getId).collect(Collectors.toList());

        if(CollectionUtil.isNotEmpty(personsVoted)){
            personsVoted = personsVoted.stream()
                    .filter((personsVote) -> personIds.contains(personsVote.getPersonId()))
                    .collect(Collectors.toList());
        }
        return personsVoted;
    }


    private List<ExVoteHistoryEntity> getByCategoryId(Long personId, List<ExVoteHistoryEntity> personsVoted) {

        ExVotePersonEntity exVotePersonEntity = exVotePersonDao.selectById(personId);
        Long categoryId = exVotePersonEntity.getCategoryId();
        if(CollectionUtil.isNotEmpty(personsVoted)){
            List<Long> personIds = personsVoted.stream().map(ExVoteHistoryEntity::getPersonId).distinct().collect(Collectors.toList());
            List<ExVotePersonEntity> exVotePersonEntities = exVotePersonDao.selectBatchIds(personIds);

            List<Long> currentCategoryPersonIds = exVotePersonEntities.stream()
                    .filter(personEntity -> categoryId.equals(personEntity.getCategoryId()))
                    .map(ExVotePersonEntity::getId).collect(Collectors.toList());

            personsVoted = personsVoted.stream()
                    .filter(personsVot -> currentCategoryPersonIds.contains(personsVot.getPersonId()))
                    .collect(Collectors.toList());
        }

        return personsVoted;
    }

    @Override
    @PreAcquireLock(value = "#{openid}")
    public void cancelVote(Long personId, String openid) {
        Date today = DateUtil.beginOfDay(new Date());
        List<ExVoteHistoryEntity> voteHistories = exVoteHistoryDao.selectList(new QueryWrapper<ExVoteHistoryEntity>()
                .select("id")
                .eq("person_id", personId)
                .eq("openid", openid)
                .ge("create_time", today));

        if (voteHistories.size() == 0) {
            throw new ServiceException("您今天还没有为TA点过赞");
        }

        exVoteHistoryDao.deleteBatchIds(voteHistories.stream()
                .map(ExVoteHistoryEntity::getId)
                .collect(Collectors.toList()));

        exVotePersonDao.decrVote(personId);
    }

    @Override
    public ExVotePersonVO detail(Long personId, String openid) {
        ExVotePersonBO person = personCache.get(personId);
        if (person == null) {
            throw new ServiceException("找不到该人员");
        }

        ExVotePersonVO personVO = new ExVotePersonVO();
        BeanUtil.copyProperties(person, personVO);

        ExVotePersonEntity voteCount = exVotePersonDao.selectOne(
                new QueryWrapper<ExVotePersonEntity>().select("vote_count")
                        .eq("id", personId));
        if (voteCount == null) {
            throw new ServiceException("找不到该人员");
        }
        personVO.setVoteCount(voteCount.getVoteCount());

        Date today = DateUtil.beginOfDay(new Date());
        List<ExVoteHistoryEntity> voteHistories = exVoteHistoryDao.selectList(new QueryWrapper<ExVoteHistoryEntity>()
                .select("id")
                .eq("person_id", personId)
                .eq("openid", openid)
                .ge("create_time", today));
        personVO.setVoteStatus(voteHistories.size() == 0);

        String intro = person.getIntro();
        List<PhotoVO> lists = JSONUtil.toList(intro, PhotoVO.class);
        lists.forEach( vo -> vo.setImageHead(imageUrl));
        personVO.setPhotos(lists);

        return personVO;
    }



    @Override
    public void save(PersonSaveParam personSaveParam) {

        // 存入json数据
        String json = JSONUtil.parseObj(personSaveParam).toString();
        FileUtil.appendUtf8String(json+"\n", new File("C:\\Users\\Administrator\\Desktop\\text.txt"));
        // 记录源数据
        ExVotePersonEntity exVotePersonEntity = new ExVotePersonEntity();
        exVotePersonEntity.setActivityId(1L);
        BeanUtil.copyProperties(personSaveParam, exVotePersonEntity);
        exVotePersonEntity.setCategoryId(getCategoryIdByCategoryName(personSaveParam.getCategoryName()));

        // 下载图片
        String fileName = RandomUtil.randomNumbers(15) + "." + FileUtil.extName(exVotePersonEntity.getHeadIcon());

        try {
            download(exVotePersonEntity.getHeadIcon(), fileName, "F:\\大学习\\活动点赞\\向上好青年\\vote1\\");
        } catch (Exception e) {
            System.out.println("下载文件失败:" + exVotePersonEntity.getHeadIcon());
            e.printStackTrace();
        }
        exVotePersonEntity.setHeadIcon("vote1/" + fileName);
        exVotePersonEntity.setVoteCount(0);
        exVotePersonEntity.setPersonNumber(getPersonNumber(exVotePersonEntity.getCategoryId()));
        exVotePersonDao.insert(exVotePersonEntity);

        ExVotePersonExtraEntity exVotePersonExtraEntity = new ExVotePersonExtraEntity();
        BeanUtil.copyProperties(personSaveParam, exVotePersonExtraEntity);
        exVotePersonExtraEntity.setPersonId(exVotePersonEntity.getId());
        exVotePersonExtraEntity.setId(null);
        exVotePersonExtraEntity.setStory(exVotePersonExtraEntity.getStory().trim());
        exVotePersonExtraDao.insert(exVotePersonExtraEntity);

    }

    private String getPersonNumber(Long categoryId) {

        QueryWrapper<ExVotePersonEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(ExVotePersonEntity::getCategoryId, categoryId);
        Integer integer = exVotePersonDao.selectCount(queryWrapper);
        if(integer == null){
            integer = 0;
        }
        integer = integer + 1;

        if(integer.toString().length() >= 2){
            return "0" + integer;
        }else{
            return "00" + integer;
        }
    }

    public static void download(String urlString, String filename,String savePath) throws Exception {
        // 构造URL
        URL url = new URL(urlString);
        // 打开连接
        URLConnection con = url.openConnection();
        //设置请求超时为5s
        con.setConnectTimeout(5*1000);
        // 输入流
        InputStream is = con.getInputStream();

        // 1K的数据缓冲
        byte[] bs = new byte[1024];
        // 读取到的数据长度
        int len;
        // 输出的文件流
        File sf=new File(savePath);
        if(!sf.exists()){
            sf.mkdirs();
        }
        // 获取图片的扩展名
        FileUtil.touch(sf.getPath()+"\\"+filename);
        OutputStream os = new FileOutputStream(sf.getPath()+"\\"+filename);
        // 开始读取
        while ((len = is.read(bs)) != -1) {
            os.write(bs, 0, len);
        }
        // 完毕，关闭所有链接
        os.close();
        is.close();
    }

    private static Map<String, Long> str = new HashMap<>();
    static {
        str.put("爱岗敬业", 1L);
        str.put("创新创业", 2L);
        str.put("勤学上进", 3L);
        str.put("扶贫助困", 4L);
        str.put("崇德守信", 5L);
        str.put("抗击疫情", 6L);
    }

    /**
     * 获取类型
     * @param categoryName
     * @return
     */
    private Long getCategoryIdByCategoryName(String categoryName) {

        QueryWrapper<ExVoteCategoryEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(ExVoteCategoryEntity::getCategoryName, categoryName);
        ExVoteCategoryEntity exVoteCategoryEntity1 = exVoteCategoryDao.selectOne(queryWrapper);

        if(exVoteCategoryEntity1 == null){
            ExVoteCategoryEntity exVoteCategoryEntity = new ExVoteCategoryEntity();
            exVoteCategoryEntity.setActivityId(1L);
            exVoteCategoryEntity.setCategoryName(categoryName);
            exVoteCategoryDao.insert(exVoteCategoryEntity);
            return exVoteCategoryEntity.getId();
        }else{
            return exVoteCategoryEntity1.getId();
        }
    }


}